/*
 Copyright (c) 2013- MeherTJ G.
 All rights reserved. Everybody can use these code freely.
 */
#pragma once

#include <string>
#include <cstdint>

namespace Zen {
	class URLUtil {
	public:
		URLUtil() = default;
		/**
		 alpha, number and the chars (-_.*): not change.
		 space : change to '+'
		 other characters: %xx
		 */
		std::string getProtocol(std::string const & url);
		
		std::string decode(void const * buf, size_t size);
		
		std::string encode(void const * buf, size_t size);

		std::string decode(std::string const &);

		std::string encode(std::string const &);

		bool isUpperHex() { return m_upper_hex; }

		void setUpperHex(bool up) { m_upper_hex = up; }

	protected:
		bool m_upper_hex = false;
	};
}


namespace Zen {
	inline std::string URLUtil::decode(const void *buf, size_t size)
	{
		std::string res;
		int state = 0;
		int v = 0;
		auto data = (unsigned char *)buf;
		for (size_t i = 0; i < size; i++)
		{
			if(state == 0)
			{
				if(data[i] == '%') state = 1;
				else if(data[i] == '+') res.push_back(0x20);
				else res.push_back(data[i]);
			}
			else if(state == 1)
			{
				v = HexValue(data[i]) << 4;
				state = 2;
			}
			else if(state == 2)
			{
				v |= HexValue(data[i]);
				res.push_back((unsigned char)v);
				state = 0;
			}
		}
		return res;
	}
	
	inline std::string URLUtil::encode(void const * buf, size_t size)
	{
		std::string res;
		
		auto data = (unsigned char const *)buf;
		for (size_t i = 0; i < size; ++i)
		{
			auto c = data[i];
			if(isalnum(c) || c == '-' || c == '_' || c == '.' || c == '*')
			{
				res.push_back((char)c);
			}
			else if(c == 0x20)
			{
				res.push_back('+');
			}
			else
			{
				res.push_back('%');
				res.push_back(HexChar(c >> 4));
				res.push_back(HexChar(c & 0xf));
			}
		}
		return res;
	}
	
	inline std::string URLUtil::getProtocol(std::string const & url)
	{
		auto pos = url.find(':');
		if(pos == url.npos) return "";
		for(decltype(pos) i = 0; i < pos; ++i)
		{
			if(!::isalnum(url[i])) return "";
		}
		return url.substr(0, pos);
	}
	
	inline std::string URLUtil::decode(std::string const & data)
	{
		return decode(data.data(), data.size());
	}
	
	inline std::string URLUtil::encode(std::string const & data)
	{
		return encode((void const *)data.data(), data.size());
	}
}

